﻿/****************************************************************************************************
	Copyright (C) 2010 RapidWebDev Organization (http://rapidwebdev.org)
	Author: Eunge, Legal Name: Jian Liu, Email: eunge.liu@RapidWebDev.org

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ****************************************************************************************************/

using System;
using System.Linq;

namespace BaoJianSoft.Platform
{
	/// <summary>
	/// The permission instance parsed from string-typed permission value. 
	/// It provides the method "Contains" to check whether a permission is included/implied in another.
	/// </summary>
	[Serializable]
	public class PermissionObject : IEquatable<PermissionObject>
	{
		private string[] segments;

		/// <summary>
		/// The explicit permission value which associated with users and roles.
		/// </summary>
		public string PermissionValue { get; set; }

		private string[] Segments
		{
			get
			{
				if (string.IsNullOrEmpty(this.PermissionValue))
					return new string[0];

				if (this.segments == null)
					this.segments = PermissionValue.Split(new string[] { "." }, StringSplitOptions.RemoveEmptyEntries);

				return this.segments;
			}
		}

		/// <summary>
		/// Constructor
		/// </summary>
		public PermissionObject()
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="permissionValue"></param>
		public PermissionObject(string permissionValue)
		{
			this.PermissionValue = permissionValue;
		}

		/// <summary>
		/// Returns true when this permission covers the other.<br/>
		/// The check algorithm is,<br/>
		/// 1) If other equals to "EveryOne" or "Anonymous", returns true. <br/>
		/// 2) Otherwise, if current permission value equals to "EveryOne" or "Anonymous", returns false. <br/>
		/// 3) Then the method compares each segment of "this" and "other" PermissionObject. 
		/// The comparing algorithm is implied in the following example. 
		/// "XXX.All" contains all permission values started with "XXX." and "XXX" itself, e.g. "XXX.Update", "XXX.Delete", "XXX.View".
		/// "XXX.YYY" contains all permission values started with "XXX.YYY" and ended with ".View", e.g. "XXX.YYY.View", "XXX.YYY.ZZZ.View".
		/// </summary>
		/// <param name="other"></param>
		/// <returns></returns>
		public bool Contains(PermissionObject other)
		{
			if (other == null) return false;
			if (string.IsNullOrEmpty(other.PermissionValue)) return false;
			if (string.Equals(other.PermissionValue, "EveryOne", StringComparison.OrdinalIgnoreCase)) return true;
			if (string.Equals(other.PermissionValue, "Anonymous", StringComparison.OrdinalIgnoreCase)) return true;

			if (string.IsNullOrEmpty(this.PermissionValue)) return false;
			if (string.Equals(this.PermissionValue, "EveryOne", StringComparison.OrdinalIgnoreCase)) return false;
			if (string.Equals(this.PermissionValue, "Anonymous", StringComparison.OrdinalIgnoreCase)) return false;

			int thisSegmentIndex = 0;
			while (thisSegmentIndex < this.Segments.Length)
			{
				string thisSegment = this.Segments[thisSegmentIndex];
				if (string.Equals(thisSegment, "All", StringComparison.OrdinalIgnoreCase))
					return true;

				string otherSegment = null;
				if (other.Segments.Length > thisSegmentIndex)
					otherSegment = other.Segments[thisSegmentIndex];

				if (otherSegment == null)
					return false;
				else if (!string.Equals(thisSegment, otherSegment, StringComparison.OrdinalIgnoreCase))
					return false;

				thisSegmentIndex++;
			}

			if (thisSegmentIndex < other.Segments.Length)
			{
				if (string.Equals(other.Segments.Last(), "View", StringComparison.OrdinalIgnoreCase))
					return true;
				else
					return false;
			}

			return true;
		}

		/// <summary>
		/// Returns explicit permission value which associated with users and roles.
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return this.PermissionValue;
		}

		/// <summary>
		/// Returns true if this instance equals to target object.
		/// </summary>
		/// <param name="obj"></param>
		/// <returns></returns>
		public override bool Equals(object obj)
		{
			if (obj == null) return false;

			return this.GetHashCode() == obj.GetHashCode();
		}

		/// <summary>
		/// Get hash code of this instance.
		/// </summary>
		/// <returns></returns>
		public override int GetHashCode()
		{
			return string.IsNullOrEmpty(this.PermissionValue) ? base.GetHashCode() : this.PermissionValue.GetHashCode();
		}

		/// <summary>
		/// Convert PermissionObject to string implicitly.
		/// </summary>
		/// <param name="permissionObject"></param>
		/// <returns></returns>
		public static implicit operator string(PermissionObject permissionObject)
		{
			return permissionObject.PermissionValue;
		}

		/// <summary>
		/// Convert string to PermissionObject implicitly.
		/// </summary>
		/// <param name="permissionObject"></param>
		/// <returns></returns>
		public static implicit operator PermissionObject(string permissionValue)
		{
			return new PermissionObject(permissionValue);
		}

		#region IEquatable<PermissionObject> Members

		bool IEquatable<PermissionObject>.Equals(PermissionObject other)
		{
			if (other == null) return false;
			return string.Equals(this.PermissionValue, other.PermissionValue, StringComparison.OrdinalIgnoreCase);
		}

		#endregion
	}
}

